1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  
22  import com.google.common.annotations.GwtCompatible;
23  import com.google.common.annotations.GwtIncompatible;
24  
25  import java.io.IOException;
26  import java.io.ObjectInputStream;
27  import java.io.ObjectOutputStream;
28  import java.util.EnumMap;
29  import java.util.Map;
30  
31  /**
32   * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
33   * are not permitted. An {@code EnumBiMap} and its inverse are both
34   * serializable.
35   * 
36   * <p>See the Guava User Guide article on <a href=
37   * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
38   * {@code BiMap}</a>.
39   *
40   * @author Mike Bostock
41   * @since 2.0 (imported from Google Collections Library)
42   */
43  @GwtCompatible(emulated = true)
44  public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>>
45      extends AbstractBiMap<K, V> {
46    private transient Class<K> keyType;
47    private transient Class<V> valueType;
48  
49    /**
50     * Returns a new, empty {@code EnumBiMap} using the specified key and value
51     * types.
52     *
53     * @param keyType the key type
54     * @param valueType the value type
55     */
56    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
57        create(Class<K> keyType, Class<V> valueType) {
58      return new EnumBiMap<K, V>(keyType, valueType);
59    }
60  
61    /**
62     * Returns a new bimap with the same mappings as the specified map. If the
63     * specified map is an {@code EnumBiMap}, the new bimap has the same types as
64     * the provided map. Otherwise, the specified map must contain at least one
65     * mapping, in order to determine the key and value types.
66     *
67     * @param map the map whose mappings are to be placed in this map
68     * @throws IllegalArgumentException if map is not an {@code EnumBiMap}
69     *     instance and contains no mappings
70     */
71    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
72        create(Map<K, V> map) {
73      EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map));
74      bimap.putAll(map);
75      return bimap;
76    }
77  
78    private EnumBiMap(Class<K> keyType, Class<V> valueType) {
79      super(WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
80          WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
81      this.keyType = keyType;
82      this.valueType = valueType;
83    }
84  
85    static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) {
86      if (map instanceof EnumBiMap) {
87        return ((EnumBiMap<K, ?>) map).keyType();
88      }
89      if (map instanceof EnumHashBiMap) {
90        return ((EnumHashBiMap<K, ?>) map).keyType();
91      }
92      checkArgument(!map.isEmpty());
93      return map.keySet().iterator().next().getDeclaringClass();
94    }
95  
96    private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) {
97      if (map instanceof EnumBiMap) {
98        return ((EnumBiMap<?, V>) map).valueType;
99      }
100     checkArgument(!map.isEmpty());
101     return map.values().iterator().next().getDeclaringClass();
102   }
103 
104   /** Returns the associated key type. */
105   public Class<K> keyType() {
106     return keyType;
107   }
108 
109   /** Returns the associated value type. */
110   public Class<V> valueType() {
111     return valueType;
112   }
113 
114   @Override
115   K checkKey(K key) {
116     return checkNotNull(key);
117   }
118 
119   @Override
120   V checkValue(V value) {
121     return checkNotNull(value);
122   }
123 
124   /**
125    * @serialData the key class, value class, number of entries, first key, first
126    *     value, second key, second value, and so on.
127    */
128   @GwtIncompatible("java.io.ObjectOutputStream")
129   private void writeObject(ObjectOutputStream stream) throws IOException {
130     stream.defaultWriteObject();
131     stream.writeObject(keyType);
132     stream.writeObject(valueType);
133     Serialization.writeMap(this, stream);
134   }
135 
136   @SuppressWarnings("unchecked") // reading fields populated by writeObject
137   @GwtIncompatible("java.io.ObjectInputStream")
138   private void readObject(ObjectInputStream stream)
139       throws IOException, ClassNotFoundException {
140     stream.defaultReadObject();
141     keyType = (Class<K>) stream.readObject();
142     valueType = (Class<V>) stream.readObject();
143     setDelegates(
144         WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
145         WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
146     Serialization.populateMap(this, stream);
147   }
148 
149   @GwtIncompatible("not needed in emulated source.")
150   private static final long serialVersionUID = 0;
151 }